import React, {PureComponent as Component} from 'react';
import PropTypes from 'prop-types';
import {Alert, Button, Col, Collapse, Icon, Input, Row, Select, Spin, Switch, Tabs, Tooltip} from 'antd';
// import constants from '../../constants/variable.js';
import AceEditor from 'client/components/AceEditor/AceEditor';
// import _ from 'underscore';
// import { isJson, deepCopyJson, json5_parse } from '../../common.js';
import {deepCopyJson, json5_parse} from '../../common.js';
// import axios from 'axios';
import ModalPostman from '../DubboModalPostman/index.js';
import CheckCrossInstall, {initCrossRequest} from './CheckCrossInstall.js';
import './Postman.scss';
// import ProjectEnv from '../../containers/DubboProject/Setting/ProjectEnv/index.js';
// import json5 from 'json5';
import _ from "underscore";
// import { connect } from 'react-redux';
import {getRunJson} from '../TreeView/Tools/MethodBodyFunc.js';
import {fetchInterfaceData} from "../../reducer/modules/dubboInterface";
import {fetchMethodData} from "../../reducer/modules/dubboMethod";
import {getProject} from "../../reducer/modules/dubboProject";

const { handleParamsValue, ArrayToObject, schemaValidator } = require('common/utils.js');
const {
  handleParams,
  crossRequest
} = require('common/dubboPostmanLib.js');


const createContext = require('common/createContext');

// const HTTP_METHOD = constants.HTTP_METHOD;
const InputGroup = Input.Group;
const Option = Select.Option;
const Panel = Collapse.Panel;

export const InsertCodeMap = [
  {
    code: 'assert.equal(status, 200)',
    title: '断言 httpCode 等于 200'
  },
  {
    code: 'assert.equal(body.code, 0)',
    title: '断言返回数据 code 是 0'
  },
  {
    code: 'assert.notEqual(status, 404)',
    title: '断言 httpCode 不是 404'
  },
  {
    code: 'assert.notEqual(body.code, 40000)',
    title: '断言返回数据 code 不是 40000'
  },
  {
    code: 'assert.deepEqual(body, {"code": 0})',
    title: '断言对象 body 等于 {"code": 0}'
  },
  {
    code: 'assert.notDeepEqual(body, {"code": 0})',
    title: '断言对象 body 不等于 {"code": 0}'
  }
];

const ParamsNameComponent = props => {
  const { example, desc, name } = props;
  const isNull = !example && !desc;
  const TooltipTitle = () => {
    return (
      <div>
        {example && (
          <div>
            示例： <span className="table-desc">{example}</span>
          </div>
        )}
        {desc && (
          <div>
            备注： <span className="table-desc">{desc}</span>
          </div>
        )}
      </div>
    );
  };

  return (
    <div>
      {isNull ? (
        <Input disabled value={name} className="key" />
      ) : (
        <Tooltip placement="topLeft" title={<TooltipTitle />}>
          <Input disabled value={name} className="key" />
        </Tooltip>
      )}
    </div>
  );
};
ParamsNameComponent.propTypes = {
  example: PropTypes.string,
  desc: PropTypes.string,
  name: PropTypes.string
};

export default class Run extends Component {
  static propTypes = {
    data: PropTypes.object, //方法原有数据
    save: PropTypes.func, //保存回调方法
    type: PropTypes.string, //enum[case, inter], 判断是在接口页面使用还是在测试集
    curUid: PropTypes.number.isRequired,
    methodId: PropTypes.number.isRequired,
    projectId: PropTypes.number.isRequired
  };

  constructor(props) {
    super(props);
    this.state = {
      loading: false,
      apiLoading: false,
      resStatusCode: null,
      test_valid_msg: null,
      resStatusText: null,
      case_env: '',
      mock_verify: false,
      enable_script: false,
      test_script: '',
      hasPlugin: true,
      inputValue: '',
      cursurPosition: { row: 1, column: -1 },
      envModalVisible: false,
      test_res_header: null,
      test_res_body: null,
      ...this.props.data
    };
  }

  checkMethodData(data) {
    if (!data || typeof data !== 'object' || !data._id) {
      return false;
    }
    return true;
  }

  selectDomain = value => {
    let headers = [];
    this.setState({
      case_env: value,
      req_headers: headers
    });
  };

  async initState(data) {
    if (!this.checkMethodData(data)) {
      console.log("data失败");
      return null;
    }
    const { inbody } = data;
    let body = inbody;
    // 运行时才会进行转换
    if (
      this.props.type === 'inter' && inbody
    ) {
      // let schema = {};
      // try {
      //   schema = json5.parse(inbody);
      // } catch (e) {
      //   return;
      // }
      // let result = await axios.post('/api/dubbomethod/schema2json', {
      //   schema: schema
      //   // required: true
      // });
      body = getRunJson(inbody);
    }

    this.setState(
      {
        ...this.state,
        // test_res_header: null,
        test_res_body: null,
        ...data,
        inbody: body,
        resStatusCode: null,
        test_valid_msg: null,
        resStatusText: null
      },
      () => this.props.type === 'inter' && this.initEnvState(data.case_env, data.evnconfig)
          // && this.initEnvState(data.case_env, data.env)
    );
  }

  initEnvState(case_env, env) {
    this.setState(
        {
          evnconfig: env
        },
        () => {
          let s = !_.find(env, item => item.cname === this.state.case_env);
          if (!this.state.case_env || s) {
            this.setState({
              case_env: this.state.evnconfig[0].cname
            });
          }
        }
    );
  }

  componentWillMount() {
    this._crossRequestInterval = initCrossRequest(hasPlugin => {
      this.setState({
        hasPlugin: hasPlugin
      });
    });
    this.initState(this.props.data);
  }

  componentWillUnmount() {
    clearInterval(this._crossRequestInterval);
  }

  componentWillReceiveProps(nextProps) {
    if (this.checkMethodData(nextProps.data) && this.checkMethodData(this.props.data)) {
      if (nextProps.data._id !== this.props.data._id) {
        this.initState(nextProps.data);
      } else if (nextProps.data.etime !== this.props.data.etime) {
        this.initState(nextProps.data);
      }
      if (nextProps.data.evnconfig !== this.props.data.evnconfig) {
        this.initEnvState(this.state.case_env, nextProps.data.evnconfig);
      }
    }
  }

  handleValue(val, global) {
    let globalValue = ArrayToObject(global);
    return handleParamsValue(val, {
      global: globalValue
    });
  }

  onOpenTest = d => {
    this.setState({
      test_script: d.text
    });
  };

  handleInsertCode = code => {
    this.aceEditor.editor.insertCode(code);
  };

  handleRequestBody = d => {
    this.setState({
      inbody: d.text
    });
  };

  reqRealMethod = async () => {
    if (this.state.loading === true) {
      this.setState({
        loading: false
      });
      return null;
    }
    this.setState({
      loading: true
    });

    let runData = this.state;
    let infData = await fetchInterfaceData(this.props.data.infid);
    let caseMethodName = "";
    if (this.props.data.method_id) {
      let methodData = await fetchMethodData(this.props.data.method_id);
      caseMethodName = methodData.payload.data.data.curMethod.mname;
    }
    let dubboData = await getProject(this.props.projectId);
    runData.url = "";
    let options = handleParams(runData, this.handleValue),
      result;
    let caseData = options.data;
    delete options.data;
    options.data = {};
    options.data.inf = infData.payload.data.data.classname;
    options.data.method = caseMethodName || this.props.data.mname;
    options.data.inname = this.props.data.inname;
    options.data.dgroup = dubboData.payload.data.data.dgroup;
    options.data.body = caseData;

    try {
      options.taskId = this.props.curUid; //有数据
      result = await crossRequest(options, this.state.pre_script, this.state.after_script, createContext(
        this.props.curUid,
        this.props.projectId,
        this.props.methodId
      ));
      result = {
        // header: result.res.header,
        body: result.res.body,
        status: result.res.status,
        statusText: result.res.statusText,
        runTime: result.runTime
      };
    } catch (data) {
      result = {
        // header: data.header,
        body: data.body,
        status: null,
        statusText: data.message
      };
    }
    if (this.state.loading === true) {
      this.setState({
        loading: false
      });
    } else {
      return null;
    }

    let tempJson = result.body;
    if (tempJson && typeof tempJson === 'object') {
      result.body = JSON.stringify(tempJson, null, '  ');
    }

    // 对 返回值数据结构 和定义的 返回数据结构 进行 格式校验
    let validResult = this.resBodyValidator(this.props.data, result.body);
    if (!validResult.valid) {
      this.setState({ test_valid_msg: `返回参数 ${validResult.message}` });
    } else {
      this.setState({ test_valid_msg: '' });
    }

    this.setState({
      resStatusCode: result.status,
      resStatusText: result.statusText,
      // test_res_header: result.header,
      test_res_body: result.body
    });
  };

    runApi = async () => {
        if (this.state.apiLoading === true) {
            this.setState({
                apiLoading: false
            });
            return null;
        }
        this.setState({
            apiLoading: true
        });

        let options = handleParams(this.state, this.handleValue),
            result;

        try {
            options.taskId = this.props.curUid; //有数据
            result = await crossRequest(options, this.state.pre_script, this.state.after_script, createContext(
                this.props.curUid,
                this.props.projectId,
                this.props.methodId
            ));
            result = {
                // header: result.res.header,
                body: result.res.body,
                status: result.res.status,
                statusText: result.res.statusText,
                runTime: result.runTime
            };
        } catch (data) {
            result = {
                // header: data.header,
                body: data.body,
                status: null,
                statusText: data.message
            };
        }
        if (this.state.apiLoading === true) {
            this.setState({
                apiLoading: false
            });
        } else {
            return null;
        }

        let tempJson = result.body;
        if (tempJson && typeof tempJson === 'object') {
            result.body = JSON.stringify(tempJson, null, '  ');
        }

        // 对 返回值数据结构 和定义的 返回数据结构 进行 格式校验
        let validResult = this.resBodyValidator(this.props.data, result.body);
        if (!validResult.valid) {
            this.setState({ test_valid_msg: `返回参数 ${validResult.message}` });
        } else {
            this.setState({ test_valid_msg: '' });
        }

        this.setState({
            resStatusCode: result.status,
            resStatusText: result.statusText,
            // test_res_header: result.header,
            test_res_body: result.body
        });
    };

  // 返回数据与定义数据的比较判断
  resBodyValidator = (methodData, test_res_body) => {
    const { outbody } = methodData;
    let validResult = { valid: true };


    const schema = json5_parse(outbody);
    const params = json5_parse(test_res_body);
    validResult = schemaValidator(schema, params);

    return validResult;
  };

  changeParam = (name, v, index, key) => {

    key = key || 'value';
    const pathParam = deepCopyJson(this.state[name]);

    pathParam[index][key] = v;
    if (key === 'value') {
      pathParam[index].enable = !!v;
    }
    this.setState({
      [name]: pathParam
    });
  };

  // 模态框的相关操作
  showModal = (val, index, type) => {
    let inputValue = '';
    let cursurPosition;
    if (type === 'inbody') {
      // req_body
      let editor = this.aceEditor.editor.editor;
      cursurPosition = editor.session.doc.positionToIndex(editor.selection.getCursor());
      // 获取选中的数据
      inputValue = this.getInstallValue(val || '', cursurPosition).val;
    } else {
      // 其他input 输入
      let oTxt1 = document.getElementById(`${type}_${index}`);
      cursurPosition = oTxt1.selectionStart;
      inputValue = this.getInstallValue(val || '', cursurPosition).val;
      // cursurPosition = {row: 1, column: position}
    }

    this.setState({
      modalVisible: true,
      inputIndex: index,
      inputValue,
      cursurPosition,
      modalType: type
    });
  };

  // 点击插入
  handleModalOk = val => {
    const { inputIndex, modalType } = this.state;
    if (modalType === 'inbody') {
      this.changeInstallBody(modalType, val);
    } else {
      this.changeInstallParam(modalType, val, inputIndex);
    }

    this.setState({ modalVisible: false });
  };

  // 根据鼠标位置往req_body中动态插入数据
  changeInstallBody = (type, value) => {
    const pathParam = deepCopyJson(this.state[type]);
    let oldValue = pathParam || '';
    let newValue = this.getInstallValue(oldValue, this.state.cursurPosition);
    let left = newValue.left;
    let right = newValue.right;
    this.setState({
      [type]: `${left}${value}${right}`
    });
  };

  // 获取截取的字符串
  getInstallValue = (oldValue, cursurPosition) => {
    let left = oldValue.substr(0, cursurPosition);
    let right = oldValue.substr(cursurPosition);

    let leftPostion = left.lastIndexOf('{{');
    let leftPostion2 = left.lastIndexOf('}}');
    let rightPostion = right.indexOf('}}');
    let val = '';
    // 需要切除原来的变量
    if (leftPostion !== -1 && rightPostion !== -1 && leftPostion > leftPostion2) {
      left = left.substr(0, leftPostion);
      right = right.substr(rightPostion + 2);
      val = oldValue.substring(leftPostion, cursurPosition + rightPostion + 2);
    }
    return {
      left,
      right,
      val
    };
  };

  // 根据鼠标位置动态插入数据
  changeInstallParam = (name, v, index, key) => {
    key = key || 'value';
    const pathParam = deepCopyJson(this.state[name]);
    let oldValue = pathParam[index][key] || '';
    let newValue = this.getInstallValue(oldValue, this.state.cursurPosition);
    let left = newValue.left;
    let right = newValue.right;
    pathParam[index][key] = `${left}${v}${right}`;
    this.setState({
      [name]: pathParam
    });
  };

  // 取消参数插入
  handleModalCancel = () => {
    this.setState({ modalVisible: false, cursurPosition: -1 });
  };

  // 环境变量模态框相关操作
  showEnvModal = () => {
    this.setState({
      envModalVisible: true
    });
  };

  handleEnvOk = (newEnv, index) => {
    this.setState({
      envModalVisible: false,
      case_env: newEnv[index].cname
    });
  };

  handleEnvCancel = () => {
    this.setState({
      envModalVisible: false
    });
  };

  render() {
    const {
      evnconfig,
      loading,
      apiLoading,
      case_env,
      inputValue,
      hasPlugin,
      isapi
    } = this.state;
    return (
      <div className="interface-test postman">
        {this.state.modalVisible && (
          <ModalPostman
            visible={this.state.modalVisible}
            handleCancel={this.handleModalCancel}
            handleOk={this.handleModalOk}
            inputValue={inputValue}
            envType={this.props.type}
            id={+this.state._id}
          />
        )}
        <CheckCrossInstall hasPlugin={hasPlugin} />

        <div className="url">
          <InputGroup compact style={{ display: 'flex' }}>
            <Select
                value={case_env}
                style={{ flexBasis: 180, flexGrow: 1 }}
                onSelect={this.selectDomain}
            >
              {evnconfig.map((item, index) => (
                <Option value={item.cname} key={index}>
                  {item.cname + '：' + item.dubbourl}
                </Option>
              ))}
            </Select>
          </InputGroup>

          <Tooltip
            placement="bottom"
            title={(() => {
              if (hasPlugin) {
                return '发送请求';
              } else {
                return '请安装 cross-request 插件';
              }
            })()}
          >
            <Button
              disabled={!hasPlugin}
              onClick={this.reqRealMethod}
              type="primary"
              style={{ marginLeft: 10 }}
              icon={loading ? 'loading' : ''}
            >
              {loading ? '取消' : '运行'}
            </Button>
          </Tooltip>

          <Tooltip
              placement="bottom"
              title={(() => {
                  if (isapi === 1) {
                      return 'Api运行';
                  } else {
                      return '该方法不是api，不能api运行';
                  }
              })()}
          >
            <Button
                disabled={isapi !== 1}
                onClick={this.runApi}
                type="primary"
                style={{ marginLeft: 10 }}
                icon={apiLoading ? 'apiLoading' : ''}
            >
              {apiLoading ? '取消' : 'Api运行'}
            </Button>
          </Tooltip>

          <Tooltip
            placement="bottom"
            title={() => {
              return this.props.type === 'inter' ? '保存到测试集' : '更新该用例';
            }}
          >
            <Button onClick={this.props.save} type="primary" style={{ marginLeft: 10 }}>
              {this.props.type === 'inter' ? '保存' : '更新'}
            </Button>
          </Tooltip>
        </div>

        <Collapse defaultActiveKey={['0', '1', '2', '3']} bordered={true}>
          <Panel
            header={
              <div style={{ display: 'flex', justifyContent: 'space-between' }}>
                <Tooltip title="F9 全屏编辑">请求报文(F9)</Tooltip>
              </div>
            }
            key="3"
              className='POST'
          >
            <div
              style={{ display: 'block' }}
            >
              {(
                <div className="adv-button">
                  <Button
                        onClick={() => this.showModal(this.state.inbody, 0, 'inbody')}
                    >
                      高级参数设置
                  </Button>
                  <Tooltip title="高级参数设置只在json字段值中生效">
                    {'  '}
                    <Icon type="question-circle-o" />
                  </Tooltip>
                </div>
              )}

              <AceEditor
                className="pretty-editor"
                ref={editor => (this.aceEditor = editor)}
                data={this.state.inbody}
                mode={ null }
                onChange={this.handleRequestBody}
                fullScreen={true}
              />
            </div>
          </Panel>
        </Collapse>

        <Tabs size="large" defaultActiveKey="res" className="response-tab">
          <Tabs.TabPane tab="Response" key="res">
            <Spin spinning={this.state.loading || this.state.apiLoading}>
              <h2
                style={{ display: this.state.resStatusCode ? '' : 'none' }}
                className={
                  'res-code ' +
                  (this.state.resStatusCode >= 200 &&
                  this.state.resStatusCode < 400 &&
                  !(this.state.loading || this.state.apiLoading)
                    ? 'success'
                    : 'fail')
                }
              >
                {this.state.resStatusCode + '  ' + this.state.resStatusText}
              </h2>
              {this.state.test_valid_msg && (
                <Alert
                  message={
                    <span>
                      Warning &nbsp;
                      <Tooltip title="针对定义为 json schema 的返回数据进行格式校验">
                        <Icon type="question-circle-o" />
                      </Tooltip>
                    </span>
                  }
                  type="warning"
                  showIcon
                  description={this.state.test_valid_msg}
                />
              )}

              <div className="container-header-body">
                <div className="body">
                  <div className="container-title">
                    <h4>Body</h4>
                  </div>
                  <AceEditor
                    readOnly={true}
                    className="pretty-editor-body"
                    data={this.state.test_res_body}
                    // mode={handleContentType(this.state.test_res_header)}
                    mode="html"
                  />
                </div>
              </div>
            </Spin>
          </Tabs.TabPane>
          {this.props.type === 'case' ? (
            <Tabs.TabPane
              className="response-test"
              tab={<Tooltip title="测试脚本，可断言返回结果，使用方法请查看文档">Test</Tooltip>}
              key="test"
            >
              <h3 style={{ margin: '5px' }}>
                &nbsp;是否开启:&nbsp;
                <Switch
                  checked={this.state.enable_script}
                  onChange={e => this.setState({ enable_script: e })}
                />
              </h3>
              <p style={{ margin: '10px' }}>注：Test 脚本只有做自动化测试才执行</p>
              <Row>
                <Col span="18">
                  <AceEditor
                    onChange={this.onOpenTest}
                    className="case-script"
                    data={this.state.test_script}
                    ref={aceEditor => {
                      this.aceEditor = aceEditor;
                    }}
                  />
                </Col>
                <Col span="6">
                  <div className="insert-code">
                    {InsertCodeMap.map(item => {
                      return (
                        <div
                          style={{ cursor: 'pointer' }}
                          className="code-item"
                          key={item.title}
                          onClick={() => {
                            this.handleInsertCode('\n' + item.code);
                          }}
                        >
                          {item.title}
                        </div>
                      );
                    })}
                  </div>
                </Col>
              </Row>
            </Tabs.TabPane>
          ) : null}
        </Tabs>
      </div>
    );
  }
}
